package com.seal.commons.infrastructure.redis.configuration.lettuce;

import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.cluster.api.StatefulRedisClusterConnection;
import io.lettuce.core.codec.ByteArrayCodec;
import io.lettuce.core.masterslave.MasterSlave;
import io.lettuce.core.masterslave.StatefulRedisMasterSlaveConnection;
import io.lettuce.core.resource.ClientResources;
import io.lettuce.core.resource.DefaultClientResources;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;

/**
 * 功能描述: <br/>
 *
 * @author yong.huang
 * @date: 2020-07-13 21:51<br/>
 * @since JDK 1.8
 */
@Configuration
@ConditionalOnClass({RedisURI.class})
@EnableConfigurationProperties({RedisProperties.class, LettuceRedisProperties.class} )
public class LettuceAutoConfiguration {
    
    @Autowired
    private RedisProperties redisProperties;

    @Autowired
    private LettuceRedisProperties lettuceRedisProperties;


    @Bean(destroyMethod = "shutdown")
    public ClientResources clientResources() {
        return DefaultClientResources.create();
    }


    @Bean
    @ConditionalOnProperty(name = "spring.redis.lettuce.mode", havingValue = "single")
    public RedisURI singleRedisUri() {
        return RedisURI.builder()
                .withHost(redisProperties.getHost())
                .withPort(redisProperties.getPort())
              //  .withPassword(redisProperties.getPassword())
                .build();
    }

    @Bean(destroyMethod = "shutdown")
    @ConditionalOnProperty(name = "spring.redis.lettuce.mode" , havingValue = "single")
    public RedisClient singleRedisClient(ClientResources clientResources, @Qualifier("singleRedisUri") RedisURI redisUri) {
        return RedisClient.create(clientResources, redisUri);
    }

    @Bean(destroyMethod = "close")
    @ConditionalOnProperty(name = "spring.redis.lettuce.mode" , havingValue = "single")
    public StatefulRedisConnection<byte[], byte[]> singleRedisConnection(@Qualifier("singleRedisClient") RedisClient singleRedisClient) {
        return singleRedisClient.connect(new ByteArrayCodec());
    }

    @Bean
    @ConditionalOnProperty(name = "spring.redis.lettuce.mode" , havingValue = "replica")
    public RedisURI replicaRedisUri() {
        return RedisURI.builder()
                .withHost(redisProperties.getHost())
                .withPort(redisProperties.getPort())
                .withPassword(redisProperties.getPassword())
                .build();
    }

    @Bean(destroyMethod = "shutdown")
    @ConditionalOnProperty(name = "spring.redis.lettuce.mode" , havingValue = "replica")
    public RedisClient replicaRedisClient(ClientResources clientResources, @Qualifier("replicaRedisUri") RedisURI redisUri) {
        return RedisClient.create(clientResources, redisUri);
    }

    @Bean(destroyMethod = "close")
    @ConditionalOnProperty(name = "spring.redis.lettuce.mode" , havingValue = "replica")
    public StatefulRedisMasterSlaveConnection<byte[], byte[]> replicaRedisConnection(@Qualifier("replicaRedisClient") RedisClient replicaRedisClient,
                                                                                     @Qualifier("replicaRedisUri") RedisURI redisUri) {
        return MasterSlave.connect(replicaRedisClient, new ByteArrayCodec(), redisUri);
    }

    @Bean
    @ConditionalOnProperty(name = "spring.redis.lettuce.mode" , havingValue = "sentinel")
    public RedisURI sentinelRedisUri() {
        List<String> nodes = redisProperties.getSentinel().getNodes();
        RedisURI.Builder builder =  RedisURI.builder();
        builder.withPassword(redisProperties.getPassword());
        if(CollectionUtils.isNotEmpty(nodes)){
            nodes.forEach(node->{
                String[] arr = node.split(":");
                builder.withSentinel(arr[0], Integer.valueOf(arr[1]));
            });
        }
        builder.withSentinelMasterId(redisProperties.getSentinel().getMaster());
        return builder.build();
    }

    @Bean(destroyMethod = "shutdown")
    @ConditionalOnProperty(name = "spring.redis.lettuce.mode" , havingValue = "sentinel")
    public RedisClient sentinelRedisClient(ClientResources clientResources, @Qualifier("sentinelRedisUri") RedisURI redisUri) {
        return RedisClient.create(clientResources, redisUri);
    }

    @Bean(destroyMethod = "close")
    @ConditionalOnProperty(name = "spring.redis.lettuce.mode" , havingValue = "sentinel")
    public StatefulRedisMasterSlaveConnection<byte[], byte[]> sentinelRedisConnection(@Qualifier("sentinelRedisClient") RedisClient sentinelRedisClient,
                                                                                    @Qualifier("sentinelRedisUri")  RedisURI redisUri){
        return MasterSlave.connect(sentinelRedisClient, new ByteArrayCodec(), redisUri);
    }



    @Bean
    @ConditionalOnProperty(name = "spring.redis.lettuce.mode" , havingValue = "cluster")
    public List<RedisURI> clusterRedisUri() {
        List<RedisURI> redisURIList = new ArrayList<>();
        List<String> nodes = redisProperties.getCluster().getNodes();
        for(String node : nodes){
            String[] arr = node.split(":");
            redisURIList.add(RedisURI.create(arr[0], Integer.valueOf(arr[1])));
        }
        return redisURIList;
    }

    @Bean(destroyMethod = "shutdown")
    @ConditionalOnProperty(name = "spring.redis.lettuce.mode" , havingValue = "cluster")
    public RedisClusterClient redisClusterClient(ClientResources clientResources, @Qualifier("clusterRedisUri") List<RedisURI> redisUri) {
        //自适应拓扑结构刷新
        ClusterTopologyRefreshOptions options = ClusterTopologyRefreshOptions.builder()
                .enableAdaptiveRefreshTrigger(
                        ClusterTopologyRefreshOptions.RefreshTrigger.MOVED_REDIRECT,
                        ClusterTopologyRefreshOptions.RefreshTrigger.PERSISTENT_RECONNECTS
                )
                .adaptiveRefreshTriggersTimeout(Duration.of(10, ChronoUnit.SECONDS))
                .build();
        RedisClusterClient redisClusterClient = RedisClusterClient.create(clientResources, redisUri);
        redisClusterClient.setOptions(ClusterClientOptions.builder().topologyRefreshOptions(options).build());
        return redisClusterClient;
    }

    @Bean(destroyMethod = "close")
    @ConditionalOnProperty(name = "spring.redis.lettuce.mode" , havingValue = "cluster")
    public StatefulRedisClusterConnection<byte[], byte[]> clusterConnection(RedisClusterClient clusterClient) {
        return clusterClient.connect(new ByteArrayCodec());
    }


}
